import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:im_app/im/service/ImMessageSendService.dart';
import 'package:im_app/im/util/DbUtil.dart';
import 'package:logger/logger.dart';

import '../../dao/ImGroupDao.dart';
import '../../dao/ImMessageDao.dart';
import '../../dao/ImSessionDao.dart';
import '../../pojo/ImPojo.dart';
import '../../service/ImSessionService.dart';
import '../../util/ImSingleton.dart';
import '../../util/MessageUtil.dart';
import 'BaseBloc.dart';
import 'MessageListCubit.dart';

final class MessageSendCubit extends BaseBloc<MessageSendEvent, MessageSendState> {
  final int sessionId;
  final MessageListCubit messageListCubit;

  final _logger = Logger(printer: PrettyPrinter(),);

  MessageSendCubit({required this.sessionId, required this.messageListCubit}) : super(MessageSendState.success(false, false)) {
    on<MessageSendShowAudioEvent>((event, emit){
      _showAudio(state, event.showAudio);
    });
    on<MessageSendShowEmojiEvent>((event, emit){
      _showEmoji(state, event.showEmoji);
    });
    on<MessageSendShowMoreEvent>((event, emit){
      _showMore(state, event.showMore);
    });
    on<MessageSendRefMessageEvent>((event, emit){
      _refMessage(state, event.refMessage);
    });
    on<MessageSendSendMessageEvent>((event, emit){
      _sendMessage(state, event.content, event.messageType);
    });
    on<MessageSendSendMessagesEvent>((event, emit){
      _sendMessages(state, event.contentList, event.messageType);
    });
    on<MessageSendMessageEvent>((event, emit){
      _messageChange(state, event.content);
    });
  }

  void _messageChange(MessageSendState oldState, String content) {
    emit(oldState.copy(message: content, refMessage: oldState.refMessage, showMore: false));
  }

  Future<void> _sendMessage(MessageSendState oldState, String content, ImMessageType? messageType) async {
    await _sendMessages(oldState, [content], messageType);
  }

  Future<void> _sendMessages(MessageSendState oldState, List<String> contentList, ImMessageType? messageType) async {
    var sessionDao = ImSessionDao();
    var messageSendService = ImMessageSendService();

    emit(oldState.copy(showEmoji: false, showMore: false, refMessage: null, isSending: true));

    try {
      ImSession? session = await sessionDao.getById(sessionId);

      var refMessage = oldState.refMessage;
      var refMessages = refMessage != null ? [refMessage] : null;

      var imMessages = <ImMessage>[];
      var length = contentList.length;
      for (var i=0; i<length; i++) {
        var content = contentList[i];
        var refs = refMessages;
        if (i != length - 1) {
          refs = null;
        }
        if (messageType == null) {
          imMessages = await messageSendService.buildMessages(session!, content, refs);
        } else {
          var message = await messageSendService.buildMessage(session!, content, messageType, refs);
          imMessages.add(message);
        }
      }

      messageListCubit.add(MessageListAddMessagesEvent(imMessages));

      if (session!.groupCode != null) {
        var groupDao = ImGroupDao();
        var sessionService = ImSessionService();
        session.group = await groupDao.getByCodeAndUserId(session.groupCode!, session.currentUserId!);
        await sessionService.initSessionInfo(session);
      }

      ImSingleton.getInstance().sessionListCubit?.changeSession(session);

      await messageSendService.sendMessage([session], imMessages);
      emit(oldState.copy(showEmoji: false, showMore: false, refMessage: null, message: '', isSending: false));
    } catch (error, stackTrace) {
      _logger.e('sendMessage error', error: error, stackTrace: stackTrace);
      if (error is Exception) {
        var exception = error as Exception;
        emit(oldState.copy(errorMsg: exception.toString(), refMessage: oldState.refMessage));
      } else {
        emit(oldState.copy(errorMsg: '发送消息失败！', refMessage: oldState.refMessage));
      }
    }
  }

  void _showEmoji(MessageSendState oldState, bool showEmoji) {
    emit(oldState.copy(showAudio: false, showEmoji: showEmoji, showMore: false, refMessage: oldState.refMessage));
  }

  void _showMore(MessageSendState oldState, bool showMore) {
    emit(oldState.copy(showAudio: false, showEmoji: false, showMore: showMore, refMessage: oldState.refMessage));
  }

  void _refMessage(MessageSendState oldState, ImMessage? refMessage) {
    var newState = oldState.copy(refMessage: refMessage);
    emit(newState);
  }

  void hideEmoji() {
    var messageSendState = MessageSendState.success(false, state.showAudio);
    emit(messageSendState);
  }

  void switchAudio() {
    var messageSendState = MessageSendState.success(false, !state.showAudio);
    emit(messageSendState);
  }

  void _showAudio(MessageSendState oldState, bool showAudio) {
    emit(oldState.copy(showAudio: showAudio, showEmoji: false, showMore: false, refMessage: oldState.refMessage));
  }

  Future<ImSession> loadSession() async {
    var sessionDao = ImSessionDao();
    var session = await sessionDao.getById(sessionId);
    return session!;
  }
}

final class MessageSendState extends Equatable {

  final bool showEmoji;
  final bool showAudio;
  final bool showMore;
  final ImMessage? refMessage;
  final String errorMsg;
  final String message;
  final bool isSending;

  const MessageSendState._({
    this.showEmoji = false,
    this.errorMsg = '',
    this.showAudio = false,
    this.showMore = false,
    this.refMessage = null,
    this.message = '',
    this.isSending = false,
  });

  MessageSendState copy({bool? showEmoji, bool? showAudio, bool? showMore, ImMessage? refMessage, String? errorMsg,
    String? message, bool? isSending }) {
    return MessageSendState._(
      showEmoji: showEmoji ?? this.showEmoji,
      showAudio: showAudio ?? this.showAudio,
      errorMsg: this.errorMsg,
      showMore: showMore ?? this.showMore,
      refMessage: refMessage,
      message: message ?? this.message,
      isSending: isSending ?? this.isSending,
    );
  }

  const MessageSendState.loading() : this._();

  const MessageSendState.success(bool showEmoji, bool showAudio)
      : this._(showEmoji: showEmoji, showAudio: showAudio);

  const MessageSendState.failure(String errorMsg) : this._(errorMsg: errorMsg);

  @override
  List<Object?> get props => [showEmoji, errorMsg, showAudio, showMore, refMessage, message];

}

abstract class MessageSendEvent {}
final class MessageSendShowAudioEvent extends MessageSendEvent {
  final bool showAudio;
  MessageSendShowAudioEvent(this.showAudio);
}
final class MessageSendShowEmojiEvent extends MessageSendEvent {
  final bool showEmoji;
  MessageSendShowEmojiEvent(this.showEmoji);
}
final class MessageSendShowMoreEvent extends MessageSendEvent {
  final bool showMore;
  MessageSendShowMoreEvent(this.showMore);
}
final class MessageSendRefMessageEvent extends MessageSendEvent {
  final ImMessage? refMessage;
  MessageSendRefMessageEvent(this.refMessage);
}
final class MessageSendSendMessageEvent extends MessageSendEvent {
  final String content;
  ImMessageType? messageType;
  MessageSendSendMessageEvent(this.content, this.messageType);
}
final class MessageSendSendMessagesEvent extends MessageSendEvent {
  final List<String> contentList;
  ImMessageType? messageType;
  MessageSendSendMessagesEvent(this.contentList, this.messageType);
}
final class MessageSendMessageEvent extends MessageSendEvent {
  final String content;
  MessageSendMessageEvent(this.content);
}